package yxy.apple.http.server;

import java.util.List;
import java.util.Map;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedTransferQueue;
import java.util.concurrent.ScheduledExecutorService;

import com.alibaba.fastjson.JSONObject;

import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpHeaderValues;
import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpObject;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpUtil;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.handler.codec.http.QueryStringDecoder;
import io.netty.util.CharsetUtil;
import lombok.extern.slf4j.Slf4j;
import yxy.apple.http.server.business.RequestDispatcher;

@Slf4j
public class HttpServerHandler extends SimpleChannelInboundHandler<HttpObject> {

	private static ScheduledExecutorService executor = null;
	private static volatile LinkedTransferQueue<QueueBean> queue = null;
	static {
		queue = new LinkedTransferQueue<>();
	}

	public HttpServerHandler(int threads) {
		if (executor == null) {
			System.err.println("创建线程池" + threads);
			executor = Executors.newScheduledThreadPool(threads);
		}
	}

	@Override
	public void channelReadComplete(ChannelHandlerContext ctx) {
		ctx.flush();
	}

	@Override
	public void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Exception {
//		log.info("have a message::" + msg.hashCode() + "::queue size::" + queue.size() + "\n{\n" + msg.toString() + "\n}");
		if (msg instanceof HttpRequest) {
			queue.put(new QueueBean(ctx, msg));
		}
	}

	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
		cause.printStackTrace();
		log.error("ctx close!", cause);
		ctx.close();
	}

	class QueueBean {
		private final ChannelHandlerContext ctx;

		private String origin;
		private String requestPath;
		private JSONObject content;

		boolean isKeepAlive;

		QueueBean(ChannelHandlerContext ctx, HttpObject msg) {
			this.ctx = ctx;
			decodeMessage(msg);
		}

		private void decodeMessage(HttpObject msg) {
			HttpRequest req = (HttpRequest) msg;
			origin = req.headers().get(HttpHeaderNames.ORIGIN);
			if (origin == null) {
				origin = "";
			}
			isKeepAlive = HttpUtil.isKeepAlive(req);
			if (HttpUtil.is100ContinueExpected(req)) {
				ctx.write(new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.CONTINUE));
			}

			// 解析http头部
			for (Map.Entry<String, String> h : req.headers()) {
				log.debug("HEADER: " + h.getKey() + " = " + h.getValue());
			}
			String uri = req.uri();
			log.debug("uri:" + uri);
			if (uri.endsWith("/favicon.ico")) {
				return;
			}
			if (uri.startsWith("http")) {
				uri = uri.replaceAll("http://[^/]+", "");
			}

			requestPath = uri.trim().split("\\?")[0];
			content = convertToMap(uri, msg);

			content.put("ip", ctx.channel().remoteAddress().toString());

			log.info("decode message::" + +msg.hashCode() + "::" + requestPath + "::::" + content);
		}

		private JSONObject convertToMap(String uri, HttpObject msg) {
			HttpRequest req = (HttpRequest) msg;
			JSONObject json = new JSONObject();
			// 是GET请求
			if (HttpMethod.GET.equals(req.method())) {
				// 解析请求参数
				QueryStringDecoder queryStringDecoder = new QueryStringDecoder(uri);
				Map<String, List<String>> paramMap = queryStringDecoder.parameters();
				for (Map.Entry<String, List<String>> entry : paramMap.entrySet()) {
					String key = entry.getKey();
					List<String> params = entry.getValue();
					if (params.size() > 1) {
						json.put(key, params);
					} else {
						json.put(key, params.get(0));
					}
				}
				return json;
			}

			// 是POST请求
			if (HttpMethod.POST.equals(req.method())) {
				String contentType = req.headers().get("Content-Type").split(";")[0];
				if (contentType.equals(HttpHeaderValues.APPLICATION_JSON.toString())) {
					FullHttpRequest fullreq = (FullHttpRequest) msg;
					String jsonStr = fullreq.content().toString(CharsetUtil.UTF_8);
					return JSONObject.parseObject(jsonStr);
				}
				if (contentType.equals(HttpHeaderValues.APPLICATION_X_WWW_FORM_URLENCODED.toString())) {
					FullHttpRequest fullreq = (FullHttpRequest) msg;
					String requestStr = fullreq.content().toString(CharsetUtil.UTF_8);
					QueryStringDecoder queryStringDecoder = new QueryStringDecoder(requestStr, CharsetUtil.UTF_8);
					String jsonStr = queryStringDecoder.uri();
					return JSONObject.parseObject(jsonStr);
//					Map<String, List<String>> paramMap = queryStringDecoder.parameters();
//					for (Map.Entry<String, List<String>> entry : paramMap.entrySet()) {
//						String key = entry.getKey();
//						List<String> params = entry.getValue();
//						if (params.size() > 1) {
//							json.put(key, params);
//						} else {
//							json.put(key, params.get(0));
//						}
//					}
//					return json;
				}
			}
			return json;
		}

		public ChannelHandlerContext getCtx() {
			return ctx;
		}

		public String getRequestPath() {
			return requestPath;
		}

		public JSONObject getContent() {
			return content;
		}
	}

	static {
		Thread thread = new Thread(new ChannelReadScan(), "ServerChannelReadScan");
		thread.setDaemon(true);
		thread.start();
	}

	private static class ChannelReadScan implements Runnable {
		@Override
		public void run() {
			try {
				while (true) {
					final QueueBean queueBean = queue.take();
					if (queueBean == null) {
						continue;
					}
					executor.execute(new Runnable() {
						@Override
						public void run() {
							ChannelHandlerContext ctx = queueBean.ctx;
							try {
								String requestPath = queueBean.requestPath;
								JSONObject content = queueBean.content;

								// 处理具体事务
								JSONObject result = service(requestPath, content);

								FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK,
										Unpooled.wrappedBuffer(result.toJSONString().getBytes()));
								response.headers().set(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.APPLICATION_JSON);
								response.headers().set(HttpHeaderNames.CONTENT_LENGTH, response.content().readableBytes());

								String origin = queueBean.origin;
								if (!origin.equals("")) {
									response.headers().set(HttpHeaderNames.ACCESS_CONTROL_ALLOW_ORIGIN, origin);
									response.headers().set(HttpHeaderNames.ACCESS_CONTROL_ALLOW_HEADERS, HttpHeaderNames.CONTENT_TYPE);
									response.headers().set(HttpHeaderNames.ACCESS_CONTROL_ALLOW_CREDENTIALS, "true");
									response.headers().set(HttpHeaderNames.ACCESS_CONTROL_ALLOW_METHODS, "POST");
								}

								if (!queueBean.isKeepAlive) {
									ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
								} else {
									response.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE);
									ctx.writeAndFlush(response);
								}
							} catch (Exception e) {
								e.printStackTrace();
							}
						}
					});
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}

		private JSONObject service(String requestPath, JSONObject content) {
			JSONObject result;
			try {
				result = RequestDispatcher.getInstance().service(requestPath, content);
				if (!result.containsKey("result")) {
					result.put("result", 0);
				}
			} catch (Exception e) {
				e.printStackTrace();
				log.error("请求处理异常{}", e.getCause().getMessage());
				result = new JSONObject();
				result.put("result", "error");
			}
			return result;
		}
	}
}
